﻿using System;
using System.Drawing;

namespace Felbot.IO {
    /// <summary>
    /// Represents a setting in a .ini-style configuration file. Can handle a "global" setting and "default" setting, and its ToInteger/String/Boolean return the best value.
    /// </summary>
    public class Setting {
        //               // local-"blah" // local-null  //
        //---------------//--------------//-------------//
        // global-"blah" // use local    // use global  //
        //---------------//--------------//-------------//
        // global-null   // use local    // null        //

        /// <summary>Creates a setting from a boolean.</summary>
        /// <param name="a_bBoolean">The boolean.</param>
        /// <returns>A new setting object.</returns>
        public static Setting FromBoolean(bool boolean) {
            if (boolean)
                return new Setting("Y");
            else
                return new Setting("N");
        }

        /// <summary>Creates a setting from an integer.</summary>
        /// <param name="a_iInteger">The integer.</param>
        /// <returns>A new setting object.</returns>
        public static Setting FromInteger(int integer) {
            return new Setting(integer.ToString());
        }

        /// <summary>Creates a setting from a string.</summary>
        /// <param name="a_sString">The string.</param>
        /// <returns>A new setting object.</returns>
        public static Setting FromString(string stringValue) {
            return new Setting(stringValue);
        }

        /// <summary>Creates a setting from a local value (can be null), a global value (can be null), and a default value (can be null).</summary>
        /// <param name="a_sSetting">The local setting.</param>
        /// <param name="a_sGlobal">The global setting.</param>
        public Setting(string setting, string global) {
            m_Setting = setting;
            m_Global = global;
        }

        /// <summary>Creates a setting from a local value (can be null).</summary>
        /// <param name="a_sSetting">The local setting.</param>
        public Setting(string setting)
            : this(setting, null) {
        }

        private string m_Setting;
        private string m_Global;

        #region To String
        /// <summary>Returns the "best" value from this setting (local setting over global setting over null).</summary>
        /// <returns>The "best" value as a string.</returns>
        public override string ToString() {
            if (m_Setting != null)
                return m_Setting;
            else
                if (m_Global != null)
                    return m_Global;
                else
                    return null;
        }

        /// <summary>Returns the "best" value from this setting (local setting over global setting over default setting provided over null).</summary>
        /// <param name="a_sDefault">The provided default value to return if no local or global setting is defined.</param>
        /// <returns>The "best" value as a string.</returns>
        public string ToString(string defaultValue) {
            if (m_Setting != null)
                return m_Setting;
            else
                if (m_Global != null)
                    return m_Global;
                else
                    return defaultValue;
        }

        /// <summary>Returns the local value from this setting (used by the configuration panel so that global settings are hidden).</summary>
        /// <returns>The local value as a string.</returns>
        public string ToLocalString() {
            return m_Setting; //if null, return null
        }

        /// <summary>Returns the local value from this setting (used by the configuration panel so that global settings are hidden).</summary>
        /// <param name="a_sDefault">The provided default value to return if no local setting is defined.</param>
        /// <returns>The local value as a string.</returns>
        public string ToLocalString(string defaultValue) {
            if (m_Setting != null)
                return m_Setting;
            else
                return defaultValue;
        }
        #endregion

        #region To FCSSetting
        /// <summary>Returns the "best" value from this setting (local setting over global setting over null).</summary>
        /// <returns>The "best" value as an FCSSetting.</returns>
        public FCSSetting ToFCSSetting() {
            if (m_Setting != null)
                return FCSSetting.FromString(m_Setting);
            else
                if (m_Global != null)
                    return FCSSetting.FromString(m_Global);
                else
                    return null;
        }

        /// <summary>Returns the "best" value from this setting (local setting over global setting over default setting provided over null).</summary>
        /// <param name="a_fcsDefault">The provided default value to return if no local or global setting is defined.</param>
        /// <returns>The "best" value as an FCSSetting.</returns>
        public FCSSetting ToFCSSetting(FCSSetting defaultValue) {
            if (m_Setting != null)
                return FCSSetting.FromString(m_Setting);
            else
                if (m_Global != null)
                    return FCSSetting.FromString(m_Global);
                else
                    return defaultValue;
        }

        /// <summary>Returns the local value from this setting (used by the configuration panel so that global settings are hidden).</summary>
        /// <returns>The local value as an FCSSetting.</returns>
        public FCSSetting ToLocalFCSSetting() {
            return FCSSetting.FromString(m_Setting); //if null, return null
        }

        /// <summary>Returns the local value from this setting (used by the configuration panel so that global settings are hidden).</summary>
        /// <param name="a_fcsDefault">The provided default value to return if no local setting is defined.</param>
        /// <returns>The local value as an FCSSetting.</returns>
        public FCSSetting ToLocalFCSSetting(FCSSetting defaultValue) {
            if (m_Setting != null)
                return FCSSetting.FromString(m_Setting);
            else
                return defaultValue;
        }
        #endregion

        #region To Int
        /// <summary>Returns the "best" value from this setting (local setting over global setting over 0).</summary>
        /// <returns>The "best" value as an integer.</returns>
        public int ToInteger() {
            try {
                return int.Parse(ToString());
            } catch (Exception) {
                return 0;
            }
        }

        /// <summary>Returns the "best" value from this setting (local setting over global setting over default setting provided over 0).</summary>
        /// <param name="a_iDefault">The provided default value to return if no local or global setting is defined.</param>
        /// <returns>The "best" value as an integer.</returns>
        public int ToInteger(int defaultValue) {
            try {
                return int.Parse(ToString());
            } catch (Exception) {
                return defaultValue;
            }
        }

        /// <summary>Returns the local value from this setting (used by the configuration panel so that global settings are hidden).</summary>
        /// <returns>The local value as an integer.</returns>
        public int ToLocalInteger() {
            try {
                return int.Parse(ToLocalString());
            } catch (Exception) {
                return 0;
            }
        }

        /// <summary>Returns the local value from this setting (used by the configuration panel so that global settings are hidden).</summary>
        /// <param name="a_iDefault">The provided default value to return if no local setting is defined.</param>
        /// <returns>The local value as an integer.</returns>
        public int ToLocalInteger(int defaultValue) {
            try {
                return int.Parse(ToLocalString());
            } catch (Exception) {
                return defaultValue;
            }
        }
        #endregion

        #region To Bool
        /// <summary>Returns the "best" value from this setting (local setting over global setting over false).</summary>
        /// <returns>The "best" value as a boolean.</returns>
        public bool ToBoolean() {
            try {
                switch (ToString().ToUpper()) {
                    case "Y":
                    case "YES":
                    case "T":
                    case "TRUE":
                        return true;
                    default: //counts N, NO, F, FALSE as well
                        return false;
                }
            } catch (Exception) {
                return false;
            }
        }

        /// <summary>Returns the "best" value from this setting (local setting over global setting over default setting provided over false).</summary>
        /// <param name="a_bDefault">The provided default value to return if no local or global setting is defined.</param>
        /// <returns>The "best" value as a boolean.</returns>
        public bool ToBoolean(bool defaultValue) {
            try {
                switch (ToString().ToUpper()) {
                    case "Y":
                    case "YES":
                    case "T":
                    case "TRUE":
                        return true;
                    case "N":
                    case "NO":
                    case "F":
                    case "FALSE":
                        return false;
                    default:
                        return defaultValue;
                }
            } catch (Exception) {
                return defaultValue;
            }
        }

        /// <summary>Returns the local value from this setting (used by the configuration panel so that global settings are hidden).</summary>
        /// <returns>The local value as a boolean.</returns>
        public bool ToLocalBoolean() {
            try {
                switch (ToLocalString().ToUpper()) {
                    case "Y":
                    case "YES":
                    case "T":
                    case "TRUE":
                        return true;
                    default: //counts N, NO, F, FALSE as well
                        return false;
                }
            } catch (Exception) {
                return false;
            }
        }

        /// <summary>Returns the local value from this setting (used by the configuration panel so that global settings are hidden).</summary>
        /// <param name="a_bDefault">The provided default value to return if no local setting is defined.</param>
        /// <returns>The local value as a boolean.</returns>
        public bool ToLocalBoolean(bool defaultValue) {
            try {
                switch (ToLocalString().ToUpper()) {
                    case "Y":
                    case "YES":
                    case "T":
                    case "TRUE":
                        return true;
                    case "N":
                    case "NO":
                    case "F":
                    case "FALSE":
                        return false;
                    default:
                        return defaultValue;
                }
            } catch (Exception) {
                return defaultValue;
            }
        }
        #endregion
    }

    /// <summary>
    /// Represents a setting for the Fonts, Colors, and Styles tab in global settings.
    /// </summary>
    public class FCSSetting {
        private string m_FontFamily;
        private FontStyle m_FontStyle;
        private Color m_ColorFore;
        private Color m_ColorBack;
        private FCSStyle m_StyleType;
        private string m_StyleCustom;

        /// <summary>Gets or sets the font face used in the style.</summary>
        public string FontFamily {
            get {
                return m_FontFamily;
            }
            set {
                m_FontFamily = value;
            }
        }

        /// <summary>Gets or sets the font style used in the style.</summary>
        public FontStyle FontStyle {
            get {
                return m_FontStyle;
            }
            set {
                m_FontStyle = value;
            }
        }

        /// <summary>Gets or sets the foreground color used in the style.</summary>
        public Color ColorFore {
            get {
                return m_ColorFore;
            }
            set {
                m_ColorFore = value;
            }
        }

        /// <summary>Gets or sets the background color used in the style.</summary>
        public Color ColorBack {
            get {
                return m_ColorBack;
            }
            set {
                m_ColorBack = value;
            }
        }

        /// <summary>Gets or sets the type of text style used in the style.</summary>
        public FCSStyle StyleType {
            get {
                return m_StyleType;
            }
            set {
                m_StyleType = value;
            }
        }

        /// <summary>Gets or sets the custom text style used in the style.</summary>
        public string StyleCustom {
            get {
                return m_StyleCustom;
            }
            set {
                m_StyleCustom = value;
            }
        }

        /// <summary>Creates a new font-color-style setting from a setting string.</summary>
        /// <param name="s">The setting string in the form FONT FAMILY;FontStyle(int);foregroundred(int),green(int),blue(int);backgroundred(int),green(int),blue(int);style(int,0=default,1=hidden,2=custom)[if custom[;CUSTOM STYLE STRING]].</param>
        /// <returns>The FCSSetting.</returns>
        public static FCSSetting FromString(string stringValue) {
            if (stringValue == null || stringValue == "")
                return new FCSSetting(null, FontStyle.Regular, Color.Empty, Color.Empty, FCSStyle.Default, null);

            //FONT FAMILY;FontStyle;forer,g,b;backr,g,b;FCSStyle[if custom{:STYLE CUSOTOM]
            string family = null;
            FontStyle style = FontStyle.Regular;
            Color fore = Color.Empty;
            Color back = Color.Empty;
            FCSStyle type = FCSStyle.Default;
            string custom = null;
            string[] splitValue = stringValue.Split(';');
            for (int i = 0; i < splitValue.Length; i++) {
                if (splitValue[i].Trim() == "")
                    continue;
                switch (i) {
                    case 0: //font: family
                        family = splitValue[i];
                        try {
                            new FontFamily(family);
                        } catch (ArgumentException) //font family does not exist
                        {
                            family = null;
                        }
                        break;
                    case 1: //font: style
                        style = (FontStyle) Enum.Parse(typeof(FontStyle), splitValue[i]);
                        break;
                    case 2: //color: fore
                        string[] cfarr = splitValue[i].Split(',');
                        try {
                            if (cfarr.Length == 1) {
                                fore = Color.FromName(splitValue[i]);
                            } else {
                                int fr = int.Parse(cfarr[0]);
                                int fg = int.Parse(cfarr[1]);
                                int fb = int.Parse(cfarr[2]);
                                fore = Color.FromArgb(fr, fg, fb);
                            }
                        } catch (ArgumentException) {
                            fore = Color.Empty;
                        }
                        break;
                    case 3: //color: back
                        string[] cbarr = splitValue[i].Split(',');
                        try {
                            if (cbarr.Length == 1) {
                                back = Color.FromName(splitValue[i]);
                            } else {
                                int br = int.Parse(cbarr[0]);
                                int bg = int.Parse(cbarr[1]);
                                int bb = int.Parse(cbarr[2]);
                                back = Color.FromArgb(br, bg, bb);
                            }
                        } catch (ArgumentException) {
                            back = Color.Empty;
                        }
                        break;
                    case 4: //style: type [, style: custom]
                        type = (FCSStyle) Enum.Parse(typeof(FCSStyle), splitValue[i]);
                        if (type == FCSStyle.Custom && splitValue[i].Length > 2) {
                            custom = splitValue[i].Substring(2);
                        }
                        break;
                }
            }

            return new FCSSetting(family, style, fore, back, type, custom);
        }

        /// <summary>Returns the setting string for this FCSSetting.</summary>
        /// <returns>The string.</returns>
        public override string ToString() {
            string family = FontFamily;
            if (FontFamily == null)
                family = "";
            int style = 0;
            if ((this.FontStyle & FontStyle.Bold) == FontStyle.Bold)
                style += 1;
            if ((this.FontStyle & FontStyle.Italic) == FontStyle.Italic)
                style += 2;
            if ((this.FontStyle & FontStyle.Underline) == FontStyle.Underline)
                style += 4;
            if ((this.FontStyle & FontStyle.Strikeout) == FontStyle.Strikeout)
                style += 8;
            string styleString = style.ToString();
            string fore;
            if (ColorFore.IsNamedColor)
                fore = ColorFore.Name;
            else if (ColorFore.IsEmpty)
                fore = "";
            else
                fore = ColorFore.R.ToString() + "," + ColorFore.G.ToString() + "," + ColorFore.B.ToString();
            string back;
            if (ColorBack.IsNamedColor)
                back = ColorBack.Name;
            else if (ColorBack.IsEmpty)
                back = "";
            else
                back = ColorBack.R.ToString() + "," + ColorBack.G.ToString() + "," + ColorBack.B.ToString();
            string type;
            switch (StyleType) {
                case FCSStyle.Default:
                default:
                    type = "0";
                    break;
                case FCSStyle.Hidden:
                    type = "1";
                    break;
                case FCSStyle.Custom:
                    if (StyleCustom == null)
                        type = "2";
                    else
                        type = "2:" + StyleCustom;
                    break;
            }

            return
                FontFamily + ";" +
                styleString + ";" +
                fore + ";" +
                back + ";" +
                type;
        }

        /// <summary>Creates a new FCSSetting.</summary>
        /// <param name="FontFamily">The font face.</param>
        /// <param name="FontStyle">The font style.</param>
        /// <param name="ColorFore">The foreground color.</param>
        /// <param name="ColorBack">The background color.</param>
        /// <param name="StyleType">The text style type.</param>
        /// <param name="StyleCustom">The text style custom value.</param>
        public FCSSetting(string family, FontStyle style, Color fore, Color back, FCSStyle type, string custom) {
            m_FontFamily = family;
            m_FontStyle = style;
            m_ColorFore = fore;
            m_ColorBack = back;
            m_StyleType = type;
            m_StyleCustom = custom;
        }
    }

    /// <summary>The FCSStyle text style enumeration.</summary>
    public enum FCSStyle {
        /// <summary>The text will appear in the default way.</summary>
        Default = 0,
        /// <summary>The text will not appear.</summary>
        Hidden = 1,
        /// <summary>The text will appear with the custom style.</summary>
        Custom = 2
    }
}
